#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：idi_mes 
@File    ：output_wip_usesn.py
@IDE     ：PyCharm 
@Author  ：孔令伟
@Date    ：9/12/24 3:44 PM 
"""
from copy import deepcopy
from datetime import datetime
from typing import Dict, List, Union

from mg_app_framework import MesCode, get_organization

from common.basic_mongo import get_mongo_collection, get_code_generation_collection
from common.constant import RetrospectType
from common.retrospect_operate.handle import filter_device_code_workorder_config
from prod_ctrl_service.tool.tool import (check_basic_param, get_time, make_retrospect_data_and_publish,
                                         check_repeat_in_or_out, insert_prod_result_param, get_ng_name,
                                         send_unqualified_http, insert_unqualified_records_backup)

"""在制品完工上报（单件、如蓝胶码等），MES检查转码后的新码、逻辑正确性及根据BOM进行扣料。"""


async def output_wip_usen_check(params: Dict[str, Union[str, int, List[Dict[str, str]]]]) \
        -> Dict[str, Union[str, Dict]]:
    """
    在制品完工上报（单件或存放单件的载具、如蓝胶/电芯/流拉框/托盘等），MES报工，通过MES逻辑检查正确性及根据BOM进行扣料。注液工序必须把注液前重量和注液后数量作为结果参数上传。
    :param params:{
           "call_id":"JQ00386",  #接口调用方认证ID, string
           "trace_id":"8941d16b-0581-4e68-8940-a651afff4282",  #接口调用ID, string
           "upperpc_code": "UP011",    #上位机编码, string
           "user_code":"Y001",  #员工编码, string
           "equip_code":"ZP001",   #设备编码, string
           "process_code":"2050",   # 工序编码, string, 必输
           "wip_list":[
                {
                "wip_no": "WW202408140002",  #产出在制品唯一码, string, 蓝胶码、电芯码、流拉框码、烤盘码等
                "src_posi_wip_no":"",  # 正极分切级极卷码
                "src_nega_wip_no":"",  # 负极分切级极卷码
                "src_up_septum":"",  # 上隔膜原料码
                "src_down_septum":"",  # 下隔膜原料码
                "src_tape":"",  # 胶带原料码
                "is_ng": 1,  #是否NG品, int, 0：OK，1：NG
                "ng_code": ["E20001","E30002"],  #NG代码, 非必输
                "param_list": [   #结果参数列表, 非必输
                    {
                     "param_code": "PM001",   #参数编码
                     "param_value": "0.95"   #参数采集值
                    },
                    {
                     "param_code": "PM002",
                     "param_value": "1"
                    }
               ]
                }
           ]
        }
    :return:{
           "code": "success",   #接口应答状态代码, success：成功; fail：失败, string
           "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
           "info": "OK"   #接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
    """
    error_msg = ""
    # '接口调用方认证ID/接口调用ID/上位机编码/员工编码/设备编码'是每一个接口都有且必填的入参，检查这些参数是否填写
    basic_param_check_result, basic_param_check_message = check_basic_param(params=params)
    if not basic_param_check_result:
        error_msg += basic_param_check_message
    process_code = params.get("process_code")
    equip_code = params.get("equip_code")
    wip_list = params.get("wip_list", [])
    if not wip_list:
        error_msg += "请检查是否已填写'产出在制品唯一码参数列表(wip_list);"
    wip_no_list = []
    for wip_list_item in wip_list:
        wip_no = wip_list_item.get("wip_no", None)
        is_ng = wip_list_item.get("is_ng", None)
        if any([wip_no is None, is_ng is None, not wip_no, is_ng not in [0, 1]]):
            error_msg += "请检查是否已填写'产出在制品唯一码(wip_no)/是否NG品(is_ng)';"
            break
        wip_no_list.append(wip_no)
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg
        }
        return result
    # 判断 产出在制品唯一码 是否在MES中已经生成
    # todo 2024-09-21 wangxl要求这段功能暂时注释
    # no_exists_code_msg = await check_rule_code_exists(wip_no_list=wip_no_list)
    # if no_exists_code_msg:
    #     error_msg += no_exists_code_msg

    # 校验是否重复出站；（如果是不良品返工，需要先给不良品打上返工标记，并限制返工次数）
    qr_code_has_used = await check_repeat_in_or_out(
        procedure_code=process_code, qr_code_list=wip_no_list, op_type=RetrospectType.PRODUCT_OUT
    )
    if qr_code_has_used:
        error_msg += f"条码在工序{process_code}上已经有出站记录"
    # 判断码值最新的进出站操作是不是本工序进站
    # latest_retros_check, latest_retros_msg = await get_qrcode_latest_retros(
    #     wip_no_list=wip_no_list, op_type=RetrospectType.PRODUCT_IN, process_code=process_code
    # )
    # if not latest_retros_check:
    #     error_msg += latest_retros_msg
    # todo Marking校验 校验在制品是否标记Marking

    workorder_config = await filter_device_code_workorder_config(device_code=equip_code)
    bom_info = workorder_config.bom_info
    if not bom_info.product_out:
        error_msg += f"设备{equip_code}的BOM信息未配置完整;"
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,
        }
        return result
    prod_result_param_list = []  # 根据入参构建的需要写入到出站参数表的数据
    unqualified_records: list = []  # 根据入参构建的需要写入到不合格品记录表的数据
    unqualified_records_backup: list = []  # 根据入参构建的需要写入到不合格品记录表的数据的备份
    for wip_record in wip_list:
        # 追溯关系建立
        ng_list = [{"code": ng_code, "num": 1} for ng_code in wip_record.get("ng_code", [])]
        # 入参新加的五个批次码数据
        code_list = []
        for field in ["src_posi_wip_no", "src_nega_wip_no", "src_up_septum", "src_down_septum", "src_tape"]:
            field_value = wip_record.get(field, None)
            if field_value not in [None, ""]:
                code_list.append(field_value)
        param = {
            "procedure_code": process_code,
            "device_code": equip_code,
            "is_ng": wip_record.get("is_ng"),
            "qr_code": wip_record.get("wip_no"),
            "ng_list": ng_list,
            # 使用bom产出物料第一个作为产出追溯的物料编码
            "material_code": bom_info.product_out[0].material_code,
            "interface_type": "output_wip_usesn",
            "code_list": code_list
        }
        retros_result, retros_msg, input_lot_no_list = await make_retrospect_data_and_publish(param=param)
        if not retros_result:
            error_msg += retros_msg
        # 构建出站参数表数据
        prod_result_param_list.append({
            "call_id": params.get("call_id"),  # 接口调用方认证ID
            "trace_id": params.get("trace_id"),  # 接口调用ID
            "upperpc_code": params.get("upperpc_code"),  # 上位机编码
            "user_code": params.get("user_code"),  # 员工编码
            "equip_code": params.get("equip_code"),  # 设备编码
            "process_code": params.get("process_code"),  # 工序编码
            "wip_no": wip_record["wip_no"],
            "param_list": wip_record["param_list"],
            "time": get_time(),
            "data_source": "036"  # 此字段用于标记由哪个接口构建的数据
        })
        is_ng = wip_record['is_ng']
        if is_ng == 1:
            # 同一个码值的不合格品记录数目取决于有多少个ng-code
            for ng_item in ng_list:
                unqualified_reason_name = await get_ng_name(ng_item['code'])
                unqualified_data = {
                    "qr_code": wip_record["wip_no"],  # 码值
                    "product_code": bom_info.product_out[0].material_code,  # 不合格品物料编码
                    "product_name": bom_info.product_out[0].material_name,  # 不合格品物料名称
                    "process_code": workorder_config.procedure_code,  # 生产工序编码
                    "process_name": workorder_config.procedure_name,  # 生产工序名称
                    "device_code": workorder_config.device_code,  # 对应生产设备编码
                    "device_name": workorder_config.device_name,  # 对应生产设备编码
                    "production_date": get_time()[:10],  # 出库日期，格式为 %Y-%m-%d
                    "unqualified_reason_code": ng_item['code'],  # 不合格原因编码（对应不合格品基础配置中的不良品编码）
                    "unqualified_reason_name": unqualified_reason_name,  # 不合格原因（对应不合格品基础配置中的不良品名称）
                    "ok_qty": None,  # 合格数
                    "ng_qty": None,  # 不合格数
                    'user_name': params.get('user_code', '')
                }
                unqualified_records.append(unqualified_data)
                unqualified_data_backup = deepcopy(unqualified_data)
                unqualified_data_backup.update({"source": '036'})
                unqualified_records_backup.append(unqualified_data_backup)

    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,
        }
        return result
    # 写入出站参数
    if prod_result_param_list:
        await insert_prod_result_param(params=prod_result_param_list)
    # 将不合格品记录发送至不合格品app
    if unqualified_records:
        await send_unqualified_http(unqualified_records)
    # 本地将不合格品记录备份写入备份表
    if unqualified_records_backup:
        await insert_unqualified_records_backup(unqualified_records_backup)
    data = {
        "call_id": params.get("call_id"),  # 接口调用方认证ID, string
        "trace_id": params.get("trace_id"),  # 接口调用ID, string
        "upperpc_code": params.get("upperpc_code"),  # 上位机编码, string
        "user_code": params.get("user_code"),  # 员工编码, string
        "equip_code": params.get("equip_code"),  # 设备编码, string
        "process_code": params.get("process_code"),  # 工序编码, string
        "wip_list": params.get("wip_list"),
        "time": get_time()
    }
    await insert_data(data=data)
    #
    for wip_list_item in wip_list:
        mes_data = {
            "src_wip_no": [],  # 极卷原料批次码
            "wip_count": "",  # 请求的批次码数量
            "parent_code": "",  # 分切的来源码值(src_wip_no的第一个码)
            "child_codes": [wip_list_item.get("wip_no")],  # 分切的码值列表
            "create_time": datetime.now()  # 创建时间
        }
        await insert_mes_data(mes_data)

    result = {
        "code": MesCode.success,  # 接口应答状态代码, success：成功; fail：失败, string
        "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
        "info": "OK",  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
    }
    return result


async def insert_data(data):
    """
    登记按电芯提交电芯状态(OK, NG)
    :param data:
    :return:
    """
    collection = get_mongo_collection(
        database_name=f"{get_organization()}_mes",
        collection_name=f"output_wip_usen_record",
    )
    await collection.insert_one(data)


async def insert_mes_data(data):
    """

    :param data:
    :return:
    """
    collection = get_code_generation_collection()
    query_filter = {
        "child_codes": {"$in": data["child_codes"]}
    }
    result = await collection.find_one(query_filter)
    if not result:
        await collection.insert_one(data)
